package main


import "fmt"
import "io/ioutil"
//import "os"
import "binchunk"
import "vm"


func main() {
	data, err := ioutil.ReadFile("test.luac")
	if err != nil {
		panic(err)
	}
	
	proto := binchunk.Undump(data)

	list(proto)
}


// 打印函数的基本信息，然后打印指令表和其它详细信息
// 最后递归调用自己把子函数信息打印出来
func list(f *binchunk.Prototype) {

	printHeader(f)

	printCode(f)

	printDetail(f)

	for _, p := range f.Protos {
		list(p)
	}

}


func printHeader(f *binchunk.Prototype) {

	funcType := "main"

	if f.LineDefined > 0 {
		funcType = "function"
	}

	varargFlag := ""

	if f.IsVararg > 0 {
		varargFlag = "+"
	}

	fmt.Printf("\n%s <%s:%d,%d> (%d instructions)\n",
		funcType, f.Source, f.LineDefined, f.LastLineDefined, len(f.Code))

	fmt.Printf("%d%s params, %d slots, %d upvalues, ",
		f.NumParams, varargFlag, f.MaxStackSize, len(f.Upvalues))

	fmt.Printf("%d locals, %d constants, %d functions\n", 
		len(f.LocVars), len(f.Constants), len(f.Protos))

}


func printCode(f *binchunk.Prototype) {

	for pc, c := range f.Code {

		line := "-"

		if len(f.LineInfo) > 0 {
			line = fmt.Sprintf("%d", f.LineInfo[pc])
		}

		// fmt.Printf("\t%d\t[%s]\t0x%08X\n", pc+1, line, c)

		i := vm.Instruction(c)

		fmt.Printf("\t%d\t[%s]\t%s \t", pc+1, line, i.OpName())

		printOperands(i)

		fmt.Printf("\n")

	}

}


// 打印指令的操作数
func printOperands(i vm.Instruction) {

	switch i.OpMode() {
	case vm.IABC: printIABCOperands(i); break
	case vm.IABx: printIABxOperands(i); break
	case vm.IAsBx: printIAsBxOperands(i); break
	case vm.IAx: printIAxOperands(i); break
	}
}


// 打印IABC指令的操作数
func printIABCOperands(i vm.Instruction) {

	a, b, c := i.ABC()

	fmt.Printf("%d", a)

	if i.BMode() != vm.OpArgN {  // 操作数不是未被使用
		if b > 0xFF {  // 操作数B的最高位是1，就认为它是常量索引，按负数输出
			fmt.Printf(" %d", -1-(b&0xFF))
		} else {
			fmt.Printf(" %d", b)
		}
	}

	if i.CMode() != vm.OpArgN {  // 操作数不是未被使用
		if c > 0xFF {  // 操作数B的最高位是1，就认为它是常量索引，按负数输出
			fmt.Printf(" %d", -1-(c&0xFF))
		} else {
			fmt.Printf(" %d", c)
		}
	}
}


// 打印IABx指令的操作数
func printIABxOperands(i vm.Instruction) {

	a, bx := i.ABx()

	fmt.Printf("%d", a)

	if i.BMode() == vm.OpArgK {  // 常量索引，按负数形式输出
		fmt.Printf(" %d", -1-bx)
	} else if i.BMode() == vm.OpArgU {
		fmt.Printf(" %d", bx)
	}
}


// 打印IAsBx指令的操作数
func printIAsBxOperands(i vm.Instruction) {

	a, sbx := i.AsBx()

	fmt.Printf("%d %d", a, sbx)
}


// 打印IAx指令的操作数
func printIAxOperands(i vm.Instruction) {

	ax := i.Ax()

	fmt.Printf("%d", -1-ax)
}


// 打印常量表、局部变量表和Upvalue表
func printDetail(f *binchunk.Prototype) {

	fmt.Printf("constants (%d):\n", len(f.Constants))

	for i, k := range f.Constants {
		fmt.Printf("\t%d\t%s\n", i+1, constantToString(k))
	}

	fmt.Printf("locals (%d):\n", len(f.LocVars))

	for i, locVar := range f.LocVars {
		fmt.Printf("\t%d\t%s\t%d\t%d\n", 
			i, locVar.VarName, locVar.StartPC+1, locVar.EndPC+1)
	}

	fmt.Printf("upvalues (%d):\n", len(f.Upvalues))

	for i, upvalue := range f.Upvalues {
		fmt.Printf("\t%d\t%s\t%d\t%d\n",
			i, upvalName(f, i), upvalue.Instack, upvalue.Idx)
	}

}


// 把常量表里的常量转化成字符串
func constantToString(k interface{}) string {

	switch k.(type) {
		case nil:	return "nil"
		case bool:	return fmt.Sprintf("%t", k)    // %t: 打印true或false
		case float64:	return fmt.Sprintf("%g", k)	 // %g: 用最少的数字来表示
		case int64:		return fmt.Sprintf("%d", k)	 // %(+)d: (带符号)整型
		case string:	return fmt.Sprintf("%q", k)  // %q: 打印单引号
		default:	return "?"
	}

}


// 根据Upvalue索引从调试信息里找出Upvalue的名字
func upvalName(f *binchunk.Prototype, idx int) string {

	if len(f.UpvalueNames) > 0 {
		return f.UpvalueNames[idx]
	}

	return "-"

}